跳到主要内容

数据绑定

使用视图模型将代码连接到编辑器中绑定的元素。

在加载 Rive 文件后,在运行时使用视图模型来连接数据。

如果你使用的是 RiveWidgetController,可以跳过创建 ViewModel 的步骤。直接转到视图模型实例

// 获取 File 和 Artboard 的引用
final file = await File.asset(
'assets/my_file.riv',
riveFactory: Factory.rive,
);
final artboard = file!.defaultArtboard()!;

// 按名称获取引用
file.viewModelByName("My View Model");

// 按索引获取引用
for (var i = 0; i < file.viewModelCount; i++) {
final indexedVM = file.viewModelByIndex(i);
}

// 获取画板的默认视图模型引用
final defaultVM = file.defaultArtboardViewModel(artboard);

// 不再使用时释放视图模型
viewModel.dispose();

视图模型实例

创建和管理视图模型的实例,用于在运行时与 Rive 图形交换数据。

如果你使用 RiveWidgetController

// 获取 File 的引用
file = await File.asset(
'assets/rewards.riv',
riveFactory: Factory.rive,
);

// 创建控制器
controller = RiveWidgetController(file!);

// 按名称进行数据绑定
viewModelInstance = controller.dataBind(DataBind.byName('My View Model'));

// 按索引进行数据绑定
viewModelInstance = controller.dataBind(DataBind.byIndex(0));

// 自动数据绑定
viewModelInstance = controller.dataBind(DataBind.auto());

// 将已有的视图模型实例绑定到控制器:
viewModelInstance = controller.dataBind(DataBind.byInstance(someViewModelInstance));

// 不再需要时释放你创建的对象
viewModelInstance.dispose();
controller.dispose();
file.dispose();

如果你想自行管理视图模型实例的创建:

final vm = file.viewModelByName("My View Model")!;

// 创建空白实例
final vmiBlank = vm.createInstance();

// 创建默认实例
final vmiDefault = vm.createDefaultInstance();

// 按索引创建
for (int i = 0; i < vm.instanceCount; i++) {
final vmiIndexed = vm.createInstanceByIndex(i);
}

// 按名称创建
final vmiNamed = vm.createInstanceByName("My Instance");

// 释放视图模型实例
viewModelInstance.dispose();

绑定

将视图模型实例绑定到画板或状态机,以建立数据连接。

如果你使用 RiveWidgetController,调用以下任一方法时会自动完成绑定:

viewModelInstance = controller.dataBind(DataBind.auto());
viewModelInstance = controller.dataBind(DataBind.byName('My View Model'));
viewModelInstance = controller.dataBind(DataBind.byIndex(0));
viewModelInstnace = controller.dataBind(someViewModelInstance);

否则,你需要手动将视图模型实例绑定到状态机或画板。

final file = await File.asset(
'assets/my_file.riv',
riveFactory: Factory.rive,
);

final artboard = file!.defaultArtboard();
final stateMachine = artboard!.defaultStateMachine()!;

final vm = file.defaultArtboardViewModel(artboard)!;
final vmi = vm.createDefaultInstance()!;

// 绑定到状态机。这会自动同时绑定到画板。
stateMachine.bindViewModelInstance(vmi);

// 如果不使用状态机,则绑定到画板
artboard.bindViewModelInstance(vmi);

自动绑定

自动绑定可简化运行时数据绑定,无需手动绑定到画板或状态机。当文件包含视图模型时,它通过索引或类型自动选择视图模型和实例。

// 获取 File 的引用
file = await File.asset(
'assets/rewards.riv',
riveFactory: Factory.rive,
);

// 创建控制器
controller = RiveWidgetController(file!);

// 自动数据绑定
viewModelInstance = controller.dataBind(DataBind.auto());

// 不再需要时释放你创建的对象
viewModelInstance.dispose();
controller.dispose();
file.dispose();

属性

属性是视图模型中可读写的数据点,用于连接到编辑器元素或在运行时从代码中更新。

列出属性

// 在 ViewModel 对象上访问
print("Properties: ${viewModel.properties}");

// 在 ViewModelInstance 对象上访问
print("Properties: ${viewModelInstance.properties}");

读写属性

// 获取 ViewModel 实例的引用
final vmi = someExistingViewModelInstance;

final numberProperty = vmi.number("My Number Property")!;
// 获取
final numberValue = numberProperty.value;

// 设置
numberProperty.value = 10;

// 监听
void onNumberChange(double value) {
print("Number changed to: $value");
}
numberProperty.addListener(onNumberChange);

// 完成后移除监听器
numberProperty.removeListener(onNumberChange);

// 或者清除所有监听器
numberProperty.clearListeners();

// 释放属性以清理资源,当你不再使用它时
// 这将在内部调用 `clearListeners()`。
numberProperty.dispose();

嵌套属性路径

使用点分隔路径或链式属性访问来访问嵌套属性。

// 获取 ViewModel 实例的引用
final vmi = someExistingViewModelInstance;

final nestedNumberByChain = vmi
.viewModel("My Nested View Model")!
.viewModel("My Second Nested VM")!
.number("My Nested Number");

final nestedNumberByPath = vmi.number("My Nested View Model/My Second Nested VM/My Nested Number");

可观察性

设置属性后,任何监听器都会收到通知,从而无需手动轮询即可支持响应式更新。

// 获取 ViewModel 实例的引用
final vmi = someExistingViewModelInstance;

final numberProperty = vmi.number("My Number Property")!;
// 获取
final numberValue = numberProperty.value;

// 设置
numberProperty.value = 10;

// 监听
void onNumberChange(double value) {
print("Number changed to: $value");
}
numberProperty.addListener(onNumberChange);

// 完成后移除监听器
numberProperty.removeListener(onNumberChange);

// 或者清除所有监听器
numberProperty.clearListeners();

// 释放属性以清理资源,当你不再使用它时
// 这将在内部调用 `clearListeners()`。
numberProperty.dispose();

图片

// 在 ViewModelInstance 对象上按路径访问图片属性
final imageProperty = viewModelInstance.image('my_image')!; // 名为 "my_image" 的图片属性

// 创建 RenderImage
final renderImage = await Factory.rive.decodeImage(bytes); // 如果使用 Flutter 渲染器,使用 `Factory.flutter`

// 如果图片有效,更新图片属性值
if (renderImage != null) {
imageProperty.value = renderImage;
}

// 你也可以将图片属性设置为 null 来清除它
imageProperty.value = null;

列表

Flutter 中的列表 API 设计类似于 Dart 中的 List 类。它不包含该类的完整 API 规范,但提供了最常用的方法。

⚠️ 警告:使用列表时,如果尝试访问越界索引或执行其他不允许的列表操作,可能会导致错误(RangeError)被抛出。与 Dart List API 类似。

ViewModelInstance 对象上按路径访问列表属性:

final todosProperty = viewModelInstance.list('todos')!; // 名为 "todos" 的列表属性
print(todosProperty.length); // 打印列表长度

要添加项目,首先需要创建要添加到列表中的视图模型实例:

final todoItemVM = riveFile.viewModelByName("TodoItem")!;
final todoItemInstance = todoItemVM.createInstance()!;

你也可以从已有实例(如 Rive 编辑器中导出的)创建实例,使用:

  • createDefaultInstance()
  • createInstanceByName('exercise')
  • createInstanceByIndex(0)

然后将实例添加到列表中:

todosProperty.add(todoItemInstance);

要从列表中移除特定实例,可以使用 remove 方法:

todosProperty.remove(todoItemInstance);

其他操作:

// 按索引移除
todosProperty.removeAt(0); // 可能抛出异常

// 按索引插入
todosProperty.insert(0, todoItemInstance); // 可能抛出异常

// 交换
todosProperty.swap(0, 1); // 可能抛出异常

// 第一个
ViewModelInstance todo = todosProperty.first(); // 可能抛出异常

// 最后一个
ViewModelInstance todo = todosProperty.last(); // 可能抛出异常

// 第一个或 null
ViewModelInstance? todo todosProperty.firstOrNull(); // 列表为空时返回 null

// 最后一个或 null
ViewModelInstance? todosProperty.lastOrNull(); // 列表为空时返回 null

// 直接按索引访问/设置
final instance = todosProperty[0]; // 可能抛出异常
todosProperty[0] = todoItemInstance; // 可能抛出异常

// 按索引获取实例
todosProperty.instanceAt(2); // 可能抛出异常

// 长度
todosProperty.length;

画板

画板属性使用 BindableArtboard 类,这与包中的常规 Artboard 类不同。

BindableArtboard 是一个运行时包装器,用于通过数据绑定与画板交互。这些实例引用文件中已有的画板,因此无需在 Rive 编辑器中额外设置。

    // 要绑定的画板属性
final artboardProp = viewModelInstance.artboard('artboardPropertyName')!;

// 创建可绑定的画板
final bindableArtboard = riveFile.artboardToBind('artboardName')!;
artboardProp.value = bindableArtboard;

枚举

// 在 File 对象上访问
print("Data enums: ${file.enums}");

示例